﻿// Copyright (c) 2012 - 2021 Jared Zheng <jaredz at outlook dot com>
// Buddha Framework is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Create : 2012-12-01 version 0.1 Jared Zheng <jaredz at outlook dot com>
// Update : 2021-03-19 version 4.0.0 Jared Zheng <jaredz at outlook dot com>

#ifndef __PACKET_STREAM_INL__
#define __PACKET_STREAM_INL__

#pragma once

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// 信令包流模板 : TPAK信令包固定数据 + 动态数据
template <typename TPAK>
INLINE CTPAKStreamBase<TPAK>::CTPAKStreamBase(void)
: TPAK()
, m_pTempStream(nullptr)
{
}

template <typename TPAK>
INLINE CTPAKStreamBase<TPAK>::~CTPAKStreamBase(void)
{
    Free();
}

template <typename TPAK>
INLINE CTPAKStreamBase<TPAK>::CTPAKStreamBase(const CTPAKStreamBase<TPAK>& aSrc)
: TPAK(aSrc)
, m_pTempStream(aSrc.m_pTempStream)
{
}

template <typename TPAK>
INLINE CTPAKStreamBase<TPAK>& CTPAKStreamBase<TPAK>::operator=(const CTPAKStreamBase<TPAK>& aSrc)
{
    if (&aSrc != this)
    {
        TPAK::operator=(aSrc);
        m_pTempStream = aSrc.m_pTempStream;
    }
    return (*this);
}

template <typename TPAK>
INLINE size_t CTPAKStreamBase<TPAK>::Length(void) const
{
    if (m_BufWrite.Tell() > 0) // 有写入数据按写入大小计算
    {
        return m_BufWrite.Tell();
    }
    else if (m_BufRead.Size() > 0)
    {
        return m_BufRead.Size();
    }
    return TPAK::Length();
}

template <typename TPAK>
INLINE void CTPAKStreamBase<TPAK>::Serialize(CStream& Stream)
{
    if (Stream.IsRead())
    {
        TPAK::Serialize(Stream);
        m_pTempStream = &Stream;
    }
    else if (m_BufWrite.Tell() > 0)
    {
        Stream.Write(m_BufWrite.GetBuf(), m_BufWrite.Tell());
    }
    else if (m_BufRead.Size() > 0)
    {
        Stream.Write(m_BufRead.GetBuf(), m_BufRead.Size());
    }
    else
    {
        TPAK::Serialize(Stream);
    }
}

template <typename TPAK>
INLINE bool CTPAKStreamBase<TPAK>::IsStream(void) const
{
    return true;
}

template <typename TPAK>
INLINE bool CTPAKStreamBase<TPAK>::Bind(CNetworkPtr& NetworkPtr, CNetParam* pNetParam)
{
    return Attach(NetworkPtr, pNetParam);
}

template <typename TPAK>
INLINE bool CTPAKStreamBase<TPAK>::IsValid(void) const
{
    return (m_AddrParam.index != nullptr);
}

template <typename TPAK>
INLINE bool CTPAKStreamBase<TPAK>::IsJumbo(void) const
{
    return ((m_AddrParam.nAttr & CNETTraits::ATTR_MAX_JUMBOBUF) != 0);
}

template <typename TPAK>
INLINE bool CTPAKStreamBase<TPAK>::AddrValid(void) const
{
    return m_AddrParam.NetAddr.IsValid();
}

template <typename TPAK>
INLINE CNetAddr& CTPAKStreamBase<TPAK>::GetAddr(void)
{
    return m_AddrParam.NetAddr;
}

template <typename TPAK>
INLINE const CNetAddr& CTPAKStreamBase<TPAK>::GetAddr(void) const
{
    return m_AddrParam.NetAddr;
}

template <typename TPAK>
INLINE void CTPAKStreamBase<TPAK>::SetAddr(CNetAddr& NetAddr)
{
    m_AddrParam.NetAddr = NetAddr;
}

template <typename TPAK>
INLINE size_t CTPAKStreamBase<TPAK>::PakSize(void) const
{
    return m_AddrParam.stSize;
}

template <typename TPAK>
INLINE size_t CTPAKStreamBase<TPAK>::BufSize(bool bIncPak) const
{
    if (bIncPak == false)
    {
        return (m_AddrParam.stCache - m_AddrParam.stSize);
    }
    return m_AddrParam.stCache;
}

template <typename TPAK>
INLINE bool CTPAKStreamBase<TPAK>::IsTempStream(void) const
{
    return (m_pTempStream != nullptr);
}

template <typename TPAK>
INLINE CStream& CTPAKStreamBase<TPAK>::GetTempStream(SeekPos sPos)
{
    if (m_pTempStream != nullptr)
    {
        if (sPos > 0)
        {
            m_pTempStream->Seek(sPos);
        }
        else if (sPos < 0)
        {
            m_pTempStream->Seek((SeekPos)m_AddrParam.stSize);
        }
        return (*m_pTempStream);
    }
    return m_BufRead;
}

template <typename TPAK>
INLINE bool CTPAKStreamBase<TPAK>::ResetPos(SeekPos sPos)
{
    return GetWrite(sPos);
}

template <typename TPAK>
INLINE CBufStreamBase& CTPAKStreamBase<TPAK>::GetStream(bool bRead, SeekPos sPos)
{
    if (bRead)
    {
        GetRead(sPos);
        return m_BufRead;
    }
    else
    {
        GetWrite(sPos, false);
        return m_BufWrite;
    }
}

template <typename TPAK>
INLINE bool CTPAKStreamBase<TPAK>::GetBuffer(CNetworkPtr& NetworkPtr, CNetStreamPtr& StreamPtr, bool bReset)
{
    assert(NetworkPtr != nullptr);
    if (IsValid() == false)
    {
        return false;
    }

    size_t stSize = 0;
    if (m_BufWrite.Size() > 0)
    {
        stSize = m_BufWrite.Tell();
    }
    else
    {
        stSize = m_BufRead.Size();
    }

    if (NetworkPtr->ReuseBuffer(StreamPtr, m_AddrParam.index, m_AddrParam.pCache) == false)
    {
        return false;
    }
    Detach();

    if (bReset)
    {
        TPAK::Serialize(*StreamPtr);
        if (stSize > m_AddrParam.stSize)
        {
            StreamPtr->Seek((SeekPos)stSize);
        }
    }
    else
    {
        StreamPtr->Seek((SeekPos)stSize);
    }
    return true;
}

template <typename TPAK>
INLINE bool CTPAKStreamBase<TPAK>::Attach(CNetworkPtr& NetworkPtr, CNetParam* pNetParam)
{
    assert(NetworkPtr != nullptr);
    assert(pNetParam != nullptr);
    if (IsValid())
    {
        Free();
    }

    if (pNetParam->pCache != nullptr)
    {
        SetAddrParam(pNetParam);
        pNetParam->pData = nullptr; // 从网络层接管缓存

        m_AddrParam.stSize = TPAK::Length();
    }
    else
    {   // 网络层缓存存在多个信令包, 需要创建一个缓存复制当前信令包数据
        if (Alloc(NetworkPtr, ((pNetParam->nAttr & CNETTraits::ATTR_MAX_JUMBOBUF) != 0)) == false)
        {
            return false;
        }
        MM_SAFE::Cpy(m_AddrParam.pData, m_AddrParam.stCache, pNetParam->pData, pNetParam->stSize);
        m_AddrParam.sSocket = pNetParam->sSocket;
    }
    if ((pNetParam->nAttr & CNETTraits::ATTR_PARAM_UDP) != 0)
    {
        m_AddrParam.NetAddr = static_cast<CAddrParam*>(pNetParam)->NetAddr;
    }
    // 读信令包流
    m_BufRead.Attach(pNetParam->stSize, m_AddrParam.pData);
    return true;
}

template <typename TPAK>
INLINE void CTPAKStreamBase<TPAK>::Detach(void)
{
    if (IsValid())
    {
        m_AddrParam.index = nullptr;
        Free();
    }
}

template <typename TPAK>
INLINE bool CTPAKStreamBase<TPAK>::Alloc(CNetworkPtr& NetworkPtr, bool bJumbo)
{
    assert(NetworkPtr != nullptr);
    if (IsValid())
    {
        Free();
    }

    if (NetworkPtr->AllocBuffer(m_AddrParam, bJumbo))
    {
        m_AddrParam.stSize = TPAK::Length();
        return true;
    }
    return false;
}

template <typename TPAK>
INLINE void CTPAKStreamBase<TPAK>::Free(void)
{
    if (m_AddrParam.pCache != nullptr)
    {
        m_BufWrite.Close();
        m_BufRead.Close();
        if (IsValid())
        {
            MObject::MCFree(m_AddrParam.index, m_AddrParam.pCache);
        }
        m_AddrParam.Reset();
        m_AddrParam.NetAddr.Reset();
    }
}

template <typename TPAK>
INLINE bool CTPAKStreamBase<TPAK>::GetRead(SeekPos sPos)
{
    if (IsValid() == false)
    {
        assert(0);
        return false;
    }
    // 如果写信令包流创建, 读信令包流为写信令包流的引用
    if (m_BufWrite.Size() > 0)
    {
        assert(m_BufRead.Size() == 0);
        m_BufRead.Refer(m_BufWrite);
    }
    if (sPos > 0)
    {
        m_BufRead.Seek(sPos);
    }
    else if (sPos == (SeekPos)-1)
    {
        m_BufRead.Seek((SeekPos)m_AddrParam.stSize);
    }
    else if (sPos == (SeekPos)-2)
    {
        m_BufRead.Seek(0);
    }
    return true;
}

template <typename TPAK>
INLINE bool CTPAKStreamBase<TPAK>::GetWrite(SeekPos sPos, bool bReset)
{
    if (IsValid() == false)
    {
        assert(0);
        return false;
    }

    if (m_BufWrite.Size() == 0)
    {
        m_BufWrite.Attach(m_AddrParam.stCache, m_AddrParam.pData);
    }
    m_BufRead.Close();
    if (bReset)
    {
        m_BufWrite.Seek(0);
        TPAK::Serialize(m_BufWrite);
        if (sPos == 0)
        {
            sPos = (SeekPos)((m_BufWrite.Tell() >= m_AddrParam.stSize) ? m_BufWrite.Tell() : m_AddrParam.stSize);
        }
        else if (sPos < 0)
        {
            sPos = (SeekPos)-1;
        }
    }
    if (sPos > 0)
    {
        m_BufWrite.Seek(sPos);
    }
    else if (sPos == (SeekPos)-1)
    {
        m_BufWrite.Seek((SeekPos)m_AddrParam.stSize);
    }
    else if (sPos == (SeekPos)-2)
    {
        m_BufWrite.Seek(0);
    }
    return true;
}

template <typename TPAK>
INLINE void CTPAKStreamBase<TPAK>::SetAddrParam(CNetParam* pNetParam)
{
    MM_SAFE::Cpy(&m_AddrParam, sizeof(CNetParam), pNetParam, sizeof(CNetParam));
}


#endif // __PACKET_STREAM_INL__
